/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.skatiger.jrove.el.implicaitobject;

import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;

public class JroveImplicitObjectResolver extends ELResolver {

    public static ELResolver makeResolver() {
        Map<String, ImplicitObject> implicits = new HashMap<String, ImplicitObject>(
                17);
        ImplicitObject io1 = new ApplicationImplicitObject();
        implicits.put(io1.getName(), io1);
        ImplicitObject io2 = new ApplicationScopeImplicitObject();
        implicits.put(io2.getName(), io2);
        ImplicitObject io3 = new CookieImplicitObject();
        implicits.put(io3.getName(), io3);
        ImplicitObject io5 = new HeaderImplicitObject();
        implicits.put(io5.getName(), io5);
        ImplicitObject io6 = new HeaderValuesImplicitObject();
        implicits.put(io6.getName(), io6);
        ImplicitObject io7 = new InitParamImplicitObject();
        implicits.put(io7.getName(), io7);
        ImplicitObject io8 = new ParamImplicitObject();
        implicits.put(io8.getName(), io8);
        ImplicitObject io9 = new ParamValuesImplicitObject();
        implicits.put(io9.getName(), io9);
        ImplicitObject io10 = new RequestImplicitObject();
        implicits.put(io10.getName(), io10);
        ImplicitObject io11 = new RequestScopeImplicitObject();
        implicits.put(io11.getName(), io11);
        ImplicitObject io12 = new SessionImplicitObject();
        implicits.put(io12.getName(), io12);
        ImplicitObject io13 = new SessionScopeImplicitObject();
        implicits.put(io13.getName(), io13);
        // jrove
        ImplicitObject io14 = new ViewContextImplicitObject();
        implicits.put(io14.getName(), io14);
        ImplicitObject io15 = new ViewScopeImplicitObject();
        implicits.put(io15.getName(), io15);
        ImplicitObject io16 = new ViewRootImplicitObject();
        implicits.put(io16.getName(), io16);
        ImplicitObject io17 = new ViewImplicitObject();
        implicits.put(io17.getName(), io17);
        // ------------
        return new JroveImplicitObjectResolver(implicits);
    }

    private Map<String, ImplicitObject> implicitObjects;

    public JroveImplicitObjectResolver() {
        super();
        this.implicitObjects = new HashMap<String, ImplicitObject>();
    }

    public JroveImplicitObjectResolver(Map<String, ImplicitObject> implicitObjects) {
        this();
        this.implicitObjects = implicitObjects;
    }

    protected String castAndIntern(Object o) {
        String s = (String) o;
        return s.intern();
    }

    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        if (base != null)
            return null;

        return String.class;
    }

    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context,
            Object base) {
        if (base != null)
            return null;

        ArrayList<FeatureDescriptor> descriptors = new ArrayList<FeatureDescriptor>(
                implicitObjects.size());

        for (ImplicitObject obj : implicitObjects.values()) {
            descriptors.add(obj.getDescriptor());
        }

        return descriptors.iterator();
    }

    public Class<?> getType(ELContext context, Object base, Object property) throws NullPointerException,
            PropertyNotFoundException,
            ELException {

        if (base != null)
            return null;
        if (property == null)
            throw new PropertyNotFoundException();
        if (!(property instanceof String))
            return null;

        String strProperty = castAndIntern(property);

        if (implicitObjects.containsKey(strProperty)) {
            context.setPropertyResolved(true);
        }

        return null;
    }

    public Object getValue(ELContext context, Object base, Object property) throws NullPointerException,
            PropertyNotFoundException,
            ELException {

        if (base != null)
            return null;
        if (property == null)
            throw new PropertyNotFoundException();
        if (!(property instanceof String))
            return null;

        String strProperty = castAndIntern(property);

        ImplicitObject obj = implicitObjects.get(strProperty);
        if (obj != null) {
            context.setPropertyResolved(true);
            return obj.getValue(context);
        }

        return null;
    }

    public boolean isReadOnly(ELContext context, Object base, Object property) throws NullPointerException,
            PropertyNotFoundException,
            ELException {

        if (base != null)
            return false;
        if (property == null)
            throw new PropertyNotFoundException();
        if (!(property instanceof String))
            return false;

        String strProperty = castAndIntern(property);

        if (implicitObjects.containsKey(strProperty)) {
            context.setPropertyResolved(true);
            return true;
        }

        return false;
    }

    public void setValue(ELContext context,
            Object base,
            Object property,
            Object value) throws NullPointerException,
            PropertyNotFoundException,
            PropertyNotWritableException,
            ELException {

        if (base != null)
            return;
        if (property == null)
            throw new PropertyNotFoundException();
        if (!(property instanceof String))
            return;

        String strProperty = castAndIntern(property);

        if (implicitObjects.containsKey(strProperty)) {
            throw new PropertyNotWritableException();
        }
    }

}
